﻿using Common;
using DAL;
using Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BLL
{
    /// <summary>
    /// 业务逻辑
    /// </summary>
    public class GDBll
    {
        /// <summary>
        /// 获取本周未完成学习分钟数
        /// </summary>
        /// <returns></returns>
        public int GetLeftMinute()
        {
            using (var db = new GDContext())
            {
                var plan = this.GetOrCreatePlan(db);
                var leanedMinutes = this.GetLeanedMinutes(plan.StartDate, plan.EndDate, db);
                int preBalanceMinutes = plan.PreBalanceMinutes.HasValue ? plan.PreBalanceMinutes.Value : 0;
                var leftMinutes = plan.PlaneMinutes + preBalanceMinutes - leanedMinutes;

                return leftMinutes;
            }
        }

        /// <summary>
        /// 保存学习时间
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        public void SaveLeaned(DateTime start, DateTime end)
        {
            start = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, start.Second);
            end = new DateTime(end.Year, end.Month, end.Day, end.Hour, end.Minute, end.Second);
            TimeSpan ts = end - start;
            int diffMinutes = (int)ts.TotalMinutes;
            if (diffMinutes < 1)
            {
                //小于1分钟就不保存了
                return;
            }

            using (var db = new GDContext())
            {
                var oldLeaned = db.Leaneds.Where(m => m.StartTime == start).FirstOrDefault();
                if (oldLeaned == null)
                {
                    Leaned leaned = new Leaned();
                    leaned.StartTime = start;
                    leaned.EndTime = start.AddMinutes(diffMinutes);

                    db.Leaneds.Add(leaned);
                }
                else
                {
                    oldLeaned.EndTime = start.AddMinutes(diffMinutes);
                }

                db.SaveChanges();
            }
        }

        /// <summary>
        /// 获取或创建学习计划并返回
        /// </summary>
        /// <returns></returns>
        private LearningPlan GetOrCreatePlan(GDContext db)
        {
            DateTime now = DateTime.Now;

            //获取本周学习计划
            var plan = db.LearningPlans.Where(m => m.StartDate <= now && m.EndDate >= now).FirstOrDefault();
            if (plan != null)
            {
                return plan;
            }

            using (var tran = db.Database.BeginTransaction())
            {
                try
                {
                    //补回到指定日期周为止缺失的学习计划
                    this.FillVacancy(now, db);

                    //计算到指定日期为止的学习情况并更新到数据库内
                    this.ComputeLeaned(now, db);

                    db.SaveChanges();
                    tran.Commit();
                }
                catch (Exception ex)
                {
                    tran.Rollback();
                    throw ex;
                }
            }

            plan = db.LearningPlans.Where(m => m.StartDate <= now && m.EndDate >= now).FirstOrDefault();
            return plan;
        }

        /// <summary>
        /// 补回到指定日期周为止缺失的学习计划（比如有几周没学习的情况）
        /// </summary>
        /// <param name="date"></param>
        /// <param name="db"></param>
        private void FillVacancy(DateTime date, GDContext db)
        {
            //最后一个学习计划
            var lastPlan = db.LearningPlans.OrderByDescending(m => m.EndDate).FirstOrDefault();

            //第一次做计划的情况
            if (lastPlan == null)
            {
                var plan = this.CreateNewLearningPlan(date);
                db.LearningPlans.Add(plan);
                db.SaveChanges();

                return;
            }

            //补缺
            while (lastPlan.EndDate < date)
            {
                DateTime newStartDate = lastPlan.EndDate.AddDays(1).Date;
                var plan = this.CreateNewLearningPlan(newStartDate);
                db.LearningPlans.Add(plan);

                lastPlan = plan;
            }
            db.SaveChanges();
        }

        /// <summary>
        /// 从指定日期开始创建一个新的学习计划
        /// </summary>
        /// <param name="start"></param>
        /// <returns></returns>
        private LearningPlan CreateNewLearningPlan(DateTime start)
        {
            DateTime date = start;

            //计划学习分钟数
            int minutes = 0;
            bool flgSunday = false;
            while (flgSunday == false)
            {
                switch (date.DayOfWeek)
                {
                    case DayOfWeek.Monday:
                        minutes += ConfigHelper.MondayMinutes;
                        break;
                    case DayOfWeek.Tuesday:
                        minutes += ConfigHelper.TuesdayMinutes;
                        break;
                    case DayOfWeek.Wednesday:
                        minutes += ConfigHelper.WednesdayMinutes;
                        break;
                    case DayOfWeek.Thursday:
                        minutes += ConfigHelper.ThursdayMinutes;
                        break;
                    case DayOfWeek.Friday:
                        minutes += ConfigHelper.FridayMinutes;
                        break;
                    case DayOfWeek.Saturday:
                        minutes += ConfigHelper.SaturdayMinutes;
                        break;
                    case DayOfWeek.Sunday:
                        minutes += ConfigHelper.SundayMinutes;
                        flgSunday = true;
                        break;
                }

                date = date.AddDays(1);
            }

            var plan = new LearningPlan();
            plan.StartDate = start.Date;
            plan.EndDate = date.Date.AddSeconds(-1);
            plan.PlaneMinutes = minutes;
            plan.PreBalanceMinutes = null;

            return plan;
        }

        /// <summary>
        /// 计算到指定日期为止的学习情况并更新到数据库内
        /// </summary>
        private void ComputeLeaned(DateTime date, GDContext db)
        {
            //获取所有未计算结余的学习计划
            var lstUncomputed = db.LearningPlans.Where(m => m.PreBalanceMinutes.HasValue == false).OrderBy(m => m.EndDate).ToList();
            if (lstUncomputed.Count == 0)
            {
                return;
            }

            //剩余未学习小时数
            int preBalanceMinutes = 0;

            //最后一个已计算结余的学习计划剩余未学习小时数
            var lastComputed = db.LearningPlans.Where(m => m.PreBalanceMinutes.HasValue).OrderByDescending(m => m.EndDate).FirstOrDefault();
            if (lastComputed != null)
            {
                var balanceMinutes = 0;
                if (lastComputed.PreBalanceMinutes.HasValue)
                {
                    balanceMinutes = lastComputed.PreBalanceMinutes.Value;
                }

                var leanedMinutes = this.GetLeanedMinutes(lastComputed.StartDate, lastComputed.EndDate, db);
                preBalanceMinutes += lastComputed.PlaneMinutes + balanceMinutes - leanedMinutes;
            }

            //循环补上结余未学习小时数
            foreach (var plan in lstUncomputed)
            {
                plan.PreBalanceMinutes = preBalanceMinutes;

                var leanedMinutes = this.GetLeanedMinutes(plan.StartDate, plan.EndDate, db);
                preBalanceMinutes += plan.PlaneMinutes - leanedMinutes;
            }
        }

        /// <summary>
        /// 获取指定时间范围内已学习小时数
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        private int GetLeanedMinutes(DateTime start, DateTime end, GDContext db)
        {
            //var minutes = db.Leaneds.Where(m => m.StartTime >= start && m.EndTime <= end).data.Sum(m => DbFunctions.ti(m.EndTime, m.StartTime));
            //return minutes.HasValue ? minutes.Value : 0;

            string sql = "SELECT SUM(TIMESTAMPDIFF(MINUTE, StartTime, EndTime)) AS minutes From leaned WHERE StartTime>=@p0 AND EndTime<=@p1";
            var minutes = db.Database.SqlQuery<int?>(sql, start, end).FirstOrDefault();
            return minutes.HasValue ? minutes.Value : 0;
        }
    }
}
